#include "core.h"

namespace core
{



/*
 * @breif: property name.
 */
const char * COMMON_CONTROL_PROPERTY_ID            = "id";
const char * COMMON_CONTROL_PROPERTY_VISIBLE       = "visible";
const char * COMMON_CONTROL_PROPERTY_TITLE         = "title";
const char * COMMON_CONTROL_PROPERTY_TABSTOP       = "tabstop";
const char * COMMON_CONTROL_PROPERTY_ZINDEX        = "z-index";
const char * COMMON_CONTROL_PROPERTY_ALPHA         = "alpha";
const char * COMMON_CONTROL_PROPERTY_DRAG          = "drag";





/*
 *@brief: FaceWindow.
 */
BEGIN_ATTRIBUTE_MAP(FaceWindow)
    ATTRIBUTE_STRING( COMMON_CONTROL_PROPERTY_ID, id_, ATTRIBUTE_TRAIT_NULL )

    ATTRIBUTE_ENUM_BEGIN( COMMON_CONTROL_PROPERTY_VISIBLE, bool )
        ATTRIBUTE_ENUM_VALUE( "visible", true )
        ATTRIBUTE_ENUM_VALUE( "hidden", false )
    ATTRIBUTE_ENUM_END( visible_, ATTRIBUTE_TRAIT_PAINT )

    ATTRIBUTE_WSTRING( COMMON_CONTROL_PROPERTY_TITLE, tip_, ATTRIBUTE_TRAIT_NULL )

    ATTRIBUTE_ENUM_BEGIN( COMMON_CONTROL_PROPERTY_TABSTOP, bool )
        ATTRIBUTE_ENUM_VALUE( "enable", true )
        ATTRIBUTE_ENUM_VALUE( "disable", false )
    ATTRIBUTE_ENUM_END( tab_stop_, ATTRIBUTE_TRAIT_NULL )

    ATTRIBUTE_ENUM_BEGIN( COMMON_CONTROL_PROPERTY_DRAG, bool )
        ATTRIBUTE_ENUM_VALUE( "enable", true )
        ATTRIBUTE_ENUM_VALUE( "disable", false )
    ATTRIBUTE_ENUM_END( drag_, ATTRIBUTE_TRAIT_NULL )

    ATTRIBUTE_INT( COMMON_CONTROL_PROPERTY_ZINDEX, z_index_, ATTRIBUTE_TRAIT_PAINT )

    ATTRIBUTE_INT( COMMON_CONTROL_PROPERTY_ALPHA, alpha_, ATTRIBUTE_TRAIT_PAINT )

    CHAIN_MEMBER_ATTRIBUTE_MAP( style_ )
END_ATTRIBUTE_MAP()

 FaceWindow::FaceWindow()
    : visible_( true )
    , tab_stop_( false )
    , z_index_( 0 )
    , alpha_( 255 )
    , drag_( false )
    , window_state_( WINDOW_STATE_CONSTRUCTED )
    , state_( FACE_STATE_NORMAL )
    , handle_delegate_( 0 )
    , parent_( 0 )
    , first_( 0 )
    , last_( 0 )
    , prev_( 0 )
    , next_( 0 )
    , message_filter_( 0 )
{
}

uint FaceWindow::DispatchMessage( uint msg, uint w_param, uint l_param )
{
    NotifyObservers( msg, w_param, l_param, true );

    uint result = 0;
    if( !message_filter_ ||
        !message_filter_->OnMessageFilter( this, msg, w_param, l_param, result ) )
    {
        ProcessMessage( msg, w_param, l_param, result );
    }

    NotifyObservers( msg, w_param, l_param, false );

    return result;
}

void FaceWindow::AddMessageObserver( IMessageObserver * observer )
{
    if( observer )
    {
        observer_.push_back( observer );
    }
}

void FaceWindow::RemoveMessageObserver( IMessageObserver *observer )
{
    _observers::iterator iter = observer_.begin();
    while( observer && iter != observer_.end() && *iter )
    {
        if( observer == *iter )
        {
            observer_.erase( iter );
            break;
        }

        ++iter;
    }
}

void FaceWindow::SetMessageFilter( IMessageFilter * filter )
{
    message_filter_ = filter;
}

void FaceWindow::NotifyObservers( uint msg, uint w_param, uint l_param, bool before )
{
    _observers::iterator iter = observer_.begin();
    while( iter != observer_.end() &&
           (*iter) )
    {
        (*iter)->OnMessageObserver( this, msg, w_param, l_param, before );
        ++iter;
    }
}

FaceWindow* FaceWindow::HitTest( int x, int y )
{
    FaceWindow * control = first_;
    while( control )
    {
        if( control->HitTest( x, y ) )
            return control;

        control = control->next_;
    }

    if( border_rect_.test( x, y ) )
    {
        return this;
    }

    return 0;
}

void FaceWindow::Destroy()
{
    FaceWindow * control = first_;
    while( control )
    {
        control->Destroy();

        FaceWindow * tmp = control->next_;
        delete control;

        control = tmp;
    }
}

std::wstring FaceWindow::Value()
{
    return L"";
}

std::wstring FaceWindow::GetInnerText()
{
    std::wstring s;

    for( FaceWindow *control = first_;
         control;
         control = control->next_ )
    {
        s += control->Value();
    }

    return s;
}

void FaceWindow::SetHandleDelegate( HandleDelegate *delegate )
{
    handle_delegate_ = delegate;

    for( FaceWindow *control = first_;
         control;
         control = control->next_ )
    {
        control->SetHandleDelegate( delegate );
    }
}

bool FaceWindow::AnalyseStyle( const char *value, const char *classname, const FaceStyleManager *manager )
{
    if( !manager )
    {
        return false;
    }

    manager->GetStyleByName( name_, style_ );

    FaceStyle faceStyle;
    if( manager->GetStyleById( id_, faceStyle ) )
        style_.Copy( faceStyle, true );

    if( classname )
    {
        faceStyle.Init();
        manager->GetStyleByClass( classname, faceStyle );
        style_.Copy( faceStyle, true );
    }

    if( value )
    {
        style_.ParseValues( value );
    }

    return true;
}

void FaceWindow::ChangeWindowState( WindowState state )
{
    window_state_ = state;
}

void FaceWindow::ChangeActionState( dword add, dword remove )
{
    dword state = state_;
    state_ |= add;
    state_ &= ~remove;

    if( state != state_ )
    {
        UpdateRect( border_rect_ );
    }
}

bool FaceWindow::ParseAttribute( const XmlElement * element, const FaceStyleManager *manager )
{
    if( !element || !manager )
    {
        return false;
    }

    std::string style;

    const XmlAttribute *pElementAttribute = element->FirstAttribute();
    while( pElementAttribute )
    {
        const char *value = pElementAttribute->Value();
        const char *name = pElementAttribute->Name();
        if( !name )
        {
            continue;
        }

        if( strcmp( "style", name ) == 0 )
        {
            style = value;
        }
        else if( strcmp( "class", name ) == 0 )
        {
            class_name_ = value;
        }
        else
        {
            uint lResult = 0;
            SetAttribute( name, value, lResult );
        }

        pElementAttribute = pElementAttribute->Next();
    }

    AnalyseStyle( style.c_str(), class_name_.c_str(), manager );

    return true;
}

bool FaceWindow::ParseChildren( const XmlElement * element, const FaceStyleManager *manager )
{
    if( !element || !manager )
    {
        return false;
    }

    const XmlNode *pNode = 0;
    while( 0 != (pNode = element->IterateChildren(pNode)) )
    {
        if( !pNode->Value() )
        {
            continue;
        }

        if( XmlNode::XML_ELEMENT == pNode->Type() )
        {
            const XmlElement * pElement = pNode->ToElement();
            const char * name = pElement->Value();
            FaceWindow *control = (FaceWindow *) RuntimeClass::CreateObject( name );
            if( !control )
            {
                continue;
            }

            control->SetName( name );
            control->style_.Inherit( style_ );

            InsertEndChild( control );

            control->DispatchMessage( FM_LOAD, (uint)pElement, (uint)manager );
        }
        else if( XmlNode::XML_TEXT == pNode->Type() )
        {
            FaceText * text = new FaceText();
            if( !text )
            {
                return false;
            }

            uint lResult = 0;
            text->SetAttribute( "value", pNode->Value(), lResult );

            text->SetName( "text" );
            text->alpha_ = alpha_;

            text->style_ = style_;

            InsertEndChild( text );
        }
    }

    return true;
}

void FaceWindow::Offset( int x, int y )
{
    boundary_rect_.offset( x, y );
    border_rect_.offset( x, y );
    content_rect_.offset( x, y );
    for( FaceWindow * control = first_;
         control;
         control = control->next_ )
    {
        control->Offset( x, y );
    }
}

void FaceWindow::Deflate( int left, int top, int right, int bottom )
{
    boundary_rect_.deflate( left, top, right, bottom );
    border_rect_.deflate( left, top, right, bottom );
    content_rect_.deflate( left, top, right, bottom );
}

void FaceWindow::Inflate( int left, int top, int right, int bottom )
{
    boundary_rect_.inflate( left, top, right, bottom );
    border_rect_.inflate( left, top, right, bottom );
    content_rect_.inflate( left, top, right, bottom );
}

void FaceWindow::UpdateRect( rect update )
{
    if( handle_delegate_ )
    {
        handle_delegate_->Update( update );
    }
}

bool FaceWindow::UpdateLayout( int x, int y, rect &update )
{
    if( x == 0 && y == 0 )
    {
        return true;
    }
    rect before_boundary = boundary_rect_;
    int oldbaseline = LayoutHelper::GetBaseline( *this, false );
    int oldcolumnline = LayoutHelper::GetColumnline( *this, false );

    update = update.unionsect( border_rect_ );
    Inflate( 0, 0, x, y );
    update = update.unionsect( border_rect_ );

    adjust just;
    just.x = x;
    just.y = y;
    just.vertical = false;

    for( FaceWindow * control = next_;
         control != 0;
         control = control->next_ )
    {
        rect rect_before = control->GetBorderRect();
        update.unionsect( rect_before );

        if( !control->DispatchMessage( FM_ADJUST, 0, (uint)&just ) )
        {
            break;
        }

        rect rect_after = control->GetBorderRect();
        update.unionsect( rect_after );
    }

    if( style_.position_ == POSITION_ABSOLUTE )
    {
        return true;
    }

    if( !parent_ )
    {
        return true;
    }

    rect after_boundary = boundary_rect_;
    int newbaseline = LayoutHelper::GetBaseline( *this, false );
    int newcolumnline = LayoutHelper::GetColumnline( *this, false );

    int parent_x = after_boundary.width() - before_boundary.width() + after_boundary.left - before_boundary.left;
    int parent_y = newbaseline - oldbaseline;
    if( parent_x == 0 && parent_y == 0 )
    {
        return true;
    }

    return parent_->UpdateLayout( parent_x, parent_y, update );
}

bool FaceWindow::UpdateLayout( rect &update )
{
    rect before_boundary = boundary_rect_;
    int oldbaseline = LayoutHelper::GetBaseline( *this, false );
    int oldcolumnline = LayoutHelper::GetColumnline( *this, false );

    /*
     * @brief: do layout by saved info.
     */
    update = border_rect_;
    layout parentLayout = parent_layout_;
    DispatchMessage( FM_LAYOUT, 0, (uint)&parentLayout );
    update = update.unionsect( border_rect_ );

    if( style_.position_ == POSITION_ABSOLUTE )
    {
        return true;
    }

    rect after_boundary = boundary_rect_;
    int newbaseline = LayoutHelper::GetBaseline( *this, false );

    int x = after_boundary.width() - before_boundary.width() + after_boundary.left - before_boundary.left;
    int y = newbaseline - oldbaseline;
    if( x == 0 && y == 0 )
    {
        return true;
    }

    adjust just;
    just.x = x;
    just.y = y;
    just.vertical = false;

    for( FaceWindow * control = next_;
         control != 0;
         control = control->next_ )
    {
        rect rect_before = control->GetBorderRect();
        update.unionsect( rect_before );

        if( !control->DispatchMessage( FM_ADJUST, 0, (uint)&just ) )
        {
            break;
        }

        rect rect_after = control->GetBorderRect();
        update.unionsect( rect_after );
    }

    int newcolumnline = LayoutHelper::GetColumnline( *this, false );

    if( !parent_ )
    {
        return true;
    }

    int parent_x = newcolumnline - oldcolumnline;
    int parent_y = y;
    if( parent_x == 0 && parent_y == 0 )
    {
        return true;
    }

    return parent_->UpdateLayout( parent_x, parent_y, update );
}

void FaceWindow::InsertBeginChild( FaceWindow *add )
{
    if( add )
    {
        add->parent_ = this;

        add->prev_ = 0;
        add->next_ = first_;

        if( first_ )
            first_->prev_ = add;
        else
            last_ = add;

        first_ = add;
    }
}

void FaceWindow::InsertEndChild( FaceWindow *add )
{
    if( add )
    {
        add->parent_ = this;

        add->prev_ = last_;
        add->next_ = 0;

        if ( last_ )
            last_->next_ = add;
        else
            first_ = add;

        last_ = add;
    }
}

void FaceWindow::InsertBeforeChild( FaceWindow *add, FaceWindow *before )
{
    if( add && before && before->parent_ == this )
    {
        add->parent_ = this;

        add->next_ = before;
        add->prev_ = before->prev_;

        if( before->prev_ )
        {
            before->prev_->next_ = add;
        }
        else
        {
            first_ = add;
        }

        before->prev_ = add;
    }
}

void FaceWindow::InsertAfterChild( FaceWindow *add, FaceWindow *after )
{
    if( add && after && after->parent_ == this )
    {
        add->parent_ = this;

        add->next_ = after->next_;
        add->prev_ = after;

        if( after->next_ )
        {
            after->next_->prev_ = add;
        }
        else
        {
            last_ = add;
        }

        after->next_ = add;
    }
}

void FaceWindow::ReplaceChild( FaceWindow *with, FaceWindow *replace )
{
    if( with && replace && replace->parent_ == this )
    {
        with->parent_ = this;
        with->next_ = replace->next_;
        with->prev_ = replace->prev_;

        if ( replace->next_ )
            replace->next_->prev_ = with;
        else
            last_ = with;

        if ( replace->prev_ )
            replace->prev_->next_ = with;
        else
            first_ = with;

        replace->Destroy();
        delete replace;
    }
}

void FaceWindow::RemoveChild( FaceWindow *remove )
{
    if( remove && remove->parent_ == this )
    {
        if ( remove->next_ )
            remove->next_->prev_ = remove->prev_;
        else
            last_ = remove->prev_;

        if ( remove->prev_ )
            remove->prev_->next_ = remove->next_;
        else
            first_ = remove->next_;

        remove->Destroy();
        delete remove;
    }
}

FaceWindow* FaceWindow::FindChild( const std::string &id )
{
    for( FaceWindow * control = first_;
         control;
         control = control->next_ )
    {
        if( control->id_ == id )
            return control;
    }

    return 0;
}

FaceWindow * FaceWindow::FindChild( const std::string &id, bool deep )
{
    for( FaceWindow * control = first_;
         control;
         control = control->next_ )
    {
        if( control->id_ == id )
        {
            return control;
        }

        if( deep )
        {
            FaceWindow * find = control->FindChild( id, deep );
            if( find )
                return find;
        }
    }

    return 0;
}

uint FaceWindow::OnLoad( uint w_param, uint l_param )
{
    XmlElement * element = (XmlElement *)w_param;
    FaceStyleManager * manager = (FaceStyleManager *)l_param;
    if( !element || !manager )
    {
        return false;
    }

    ParseAttribute( element, manager );

    ParseChildren( element, manager );

    ChangeWindowState( WINDOW_STATE_LOADED );
    return true;
}

uint FaceWindow::OnLayout( uint w_param, uint l_param )
{
    layout * parentLayout = (layout *)l_param;
    if( !parentLayout )
    {
        return false;
    }

    if( window_state_ < WINDOW_STATE_LAYOUTED )
    {
        parent_layout_ = *parentLayout;
    }

    boundary_rect_.empty();
    border_rect_.empty();
    content_rect_.empty();
    layout thisLayout;

    FaceStreamLayout layoutor;
    layoutor.BeforeBlockLayout( *parentLayout, *this, boundary_rect_, border_rect_, content_rect_, thisLayout );

    for( FaceWindow *control = first_;
         control;
         control = control->next_ )
    {
        thisLayout.leaf = false;
        if( !control->DispatchMessage( FM_LAYOUT, w_param, (uint)&thisLayout ) )
        {
            break;
        }
    }

    layoutor.AfterBlockLayout( *parentLayout, *this, boundary_rect_, border_rect_, content_rect_, thisLayout );

    ChangeWindowState( WINDOW_STATE_LAYOUTED );
    return true;
}

uint FaceWindow::OnAdjust( uint, uint l_param )
{
    adjust * just = (adjust *)l_param;
    if( !just )
    {
        return false;
    }

    int x = 0;
    int y = 0;

    if( just->vertical )
    {
        y = just->y;
    }
    else
    {
        x = just->x;
    }

    if( x == 0 && y == 0 )
    {
        return false;
    }

    Offset( x, y );

    parent_layout_.col += x;
    parent_layout_.row += y;
    parent_layout_.baseline += y;

    return true;
}

uint FaceWindow::OnPaint( uint w_param, uint l_param )
{
    FacePainter * painter = ( FacePainter * )w_param;
    rect * clip = (rect *)l_param;

    int nclip = BeginPaint( painter, clip );
    if( nclip == 0 )
    {
        return true;
    }

    Draw( painter );

    DrawChild( painter, clip );

    EndPaint( painter, nclip );

    ChangeWindowState( WINDOW_STATE_READY );

    return true;
}

uint FaceWindow::OnMouseHover( uint w_param, uint l_param )
{
    ChangeActionState( FACE_STATE_HOVER, 0 );
    return true;
}

uint FaceWindow::OnMouseLeave( uint w_param, uint l_param )
{
    ChangeActionState( 0, FACE_STATE_NORMAL );
    return true;
}

uint FaceWindow::OnLButtonDown( uint w_param, uint l_param )
{
    ChangeActionState( FACE_STATE_DOWN | FACE_STATE_FOCUS, 0 );
    if( drag_ )
    {
        if( handle_delegate_ )
        {
            handle_delegate_->Drag();
        }
    }
    return true;
}

uint FaceWindow::OnLButtonUp( uint w_param, uint l_param )
{
    ChangeActionState( 0, FACE_STATE_HOVER );
    return true;
}

int FaceWindow::BeginPaint( FacePainter *painter, rect *clip )
{
    if( !painter || !clip || !clip->contains( border_rect_ ) )
    {
        return 0;
    }

    return painter->SetClipRect( border_rect_ );
}

void FaceWindow::EndPaint( FacePainter *painter, int clip )
{
    if( painter )
    {
        painter->ClearClipRect( clip );
    }
}

void FaceWindow::Draw( FacePainter *painter )
{
    if( style_.border_width_ > 0 )
    {
        painter->DrawBorder( border_rect_, style_.border_style_, style_.border_width_, style_.border_color_, alpha_ );
    }

    rect background;
    LayoutHelper::GetBackgroundRect( style_, border_rect_, background );

    if( !style_.Default( FaceStyle::PROPERTY_BACKGROUND_COLOR ) )
    {
        painter->DrawBackground( background, style_.background_color_, alpha_ );
    }
    if( !style_.background_image_.empty() )
    {
        painter->DrawImage( style_.background_image_, background, alpha_ );
    }
}

void FaceWindow::DrawChild( FacePainter *painter, rect *clip )
{
    for( FaceWindow *control = first_;
        control;
        control = control->next_ )
    {
        control->DispatchMessage( FM_PAINT, (uint)painter, (uint)clip );
    }
}





/*
 *@breif: FaceText
 */
BEGIN_ATTRIBUTE_MAP(FaceText)
    ATTRIBUTE_WSTRING( "value", value_, ATTRIBUTE_TRAIT_PAINT | ATTRIBUTE_TRAIT_LAYOUT )
END_ATTRIBUTE_MAP()

void FaceText::Offset( int x, int y )
{
    for( _tokens::iterator iter = tokens_.begin();
         iter != tokens_.end();
         iter++ )
    {
        iter->rect.offset( x, y );
    }
}

uint FaceText::OnLayout( uint wParam, uint lParam )
{
    layout * parentLayout = (layout *)lParam;
    if( !parentLayout )
    {
        return false;
    }

    tokens_.clear();
    FacePainter painter(0);
    int rest = 0;
    int maxWidth = 0;
    if( (parentLayout->fit & FIT_TYPE_WIDTH) == FIT_TYPE_WIDTH )
    {
        maxWidth = parentLayout->width;
        rest = parentLayout->width + parentLayout->content.left - parentLayout->col;
    }
    else
    {
        maxWidth = parentLayout->content.width();
        rest = parentLayout->content.right - parentLayout->col;
    }

    parentLayout->baseline = parentLayout->row + style_.line_height_;

    token tken;
    int begin = 0;
    int position = 0;

    do
    {
        std::wstring subValue = value_.substr( begin );
        position = painter.GetCharacterPosition( subValue, rest, style_.font_family_, style_.font_size_, style_.font_style_ );
        if( position == 0 )
        {
            break;
        }

        tken.rect.left = parentLayout->col;
        tken.rect.right = parentLayout->col + rest;
        tken.rect.top = parentLayout->row;
        tken.rect.bottom = parentLayout->baseline;
        tken.begin = begin;
        tken.end = position;
        tokens_.push_back( tken );

        begin += position;

        if( position == subValue.size() )
        {
            parentLayout->col += rest;

            parentLayout->boundary.left = min( parentLayout->boundary.left, parentLayout->col );
            parentLayout->boundary.right = max( parentLayout->boundary.right, parentLayout->col );
            parentLayout->boundary.top = min( parentLayout->boundary.top, parentLayout->row );
            parentLayout->boundary.bottom = max( parentLayout->boundary.bottom, parentLayout->baseline );
            break;
        }
        else
        {
            parentLayout->boundary.left = min( parentLayout->boundary.left, parentLayout->content.left );
            parentLayout->boundary.right = max( parentLayout->boundary.right, parentLayout->col + rest );

            parentLayout->col = parentLayout->content.left;
            parentLayout->row += style_.line_height_;
            parentLayout->baseline += style_.line_height_;

            parentLayout->boundary.top = min( parentLayout->boundary.top, parentLayout->row );
            parentLayout->boundary.bottom = max( parentLayout->boundary.bottom, parentLayout->baseline );

            rest = maxWidth;
        }

    } while (true);

    ChangeWindowState( WINDOW_STATE_LAYOUTED );

    return true;
}

uint FaceText::OnPaint( uint wParam, uint lParam )
{
    FacePainter * painter = ( FacePainter * )wParam;
    rect *clip = ( rect * )lParam;
    if( !painter )
    {
        return false;
    }

    dword font_style_ = style_.font_style_;
    if( style_.underline_ )
    {
        font_style_ |= core::FONT_STYLE_UNDERLINE;
    }
    for( _tokens::iterator iter = tokens_.begin();
         iter != tokens_.end();
         iter++ )
    {
        if( clip && !clip->contains( iter->rect ) )
        {
            continue;
        }

        int nclip = painter->SetClipRect( iter->rect );

        std::wstring strText = value_.substr( iter->begin, iter->end - iter->begin );
        painter->DrawString(
            strText,
            iter->rect,
            style_.font_family_,
            style_.font_size_,
            font_style_,
            style_.text_color_,
            style_.text_alignment_,
            alpha_ );

        painter->ClearClipRect( nclip );
    }

    ChangeWindowState( WINDOW_STATE_READY );

    return true;
}



};